﻿using System;
using System.Collections.Generic;
using UnityEngine;

namespace Core
{

    [Skill]
    public class SkillSystem : ISkill
    {
        protected float startTime;
        protected bool isTriggerSkill = false;
        protected bool isStartSkill = false;
        protected float cdTime;
        protected Unit caster;
        protected SkillConfig config;
        protected SkillState skillState;
        protected ISkillTrigger skillTrigger;
        private SkillArgs args;

        bool IObject.IsDisposed => config == null;

        void ISkill.Awake(Unit caster, int skillId)
        {
            var config = World.GetComponent<ConfigComponent>().GetConfig<SkillConfig>(skillId);
            if (config == null || !config.isVaild)
                return;

            this.config = config;
            this.caster = caster;

            skillTrigger = World.GetComponent<SkillTriggerComponent>().CreateSkillTrigger(this.config.trigger, this);

            OnAwake();
        }

        public virtual float GetCDTime()
        {
            return cdTime - Time.time;
        }

        public virtual bool IsCDVaild()
        {
            return cdTime > 0;
        }

        void ISkill.Start(SkillArgs args)
        {
            this.args = args;
            skillState = SkillState.Start;
            cdTime = 1 / 60 * config.cdTime + Time.time;
            isStartSkill = true;
            isTriggerSkill = false;
            startTime = Time.time;

            caster.GetComponent<StateMachineComponent>().ChangedState(StateType.Attack);
        }
        void IUpdate.Update()
        {
            if (!isStartSkill)
                return;

            skillState = SkillState.Running;

            if(!isTriggerSkill && Time.time >= startTime + config.triggerTimer)
            {
                OnTrigger();
                isTriggerSkill = true;
            }

            if(Time.time >= startTime + config.continueTimer)
            {
                (this as ISkill).Stop();
            }
        }

        void OnTrigger()
        {
            if (config.bulletId != 0)
            {
                var bullet = World.GetComponent<BulletComponent>().CreateBullet(caster, config.bulletId);
                bullet.Launch(caster.transform.position, caster.transform.forward);
            }
            else
            {
                DamageByAttack();
            }

            if(config.bulletId != 0)
            {
                caster.GetComponent<BuffComponent>()?.AddBuff(config.buffId);
            }
        }

        void IDisposable.Dispose()
        {
            caster = null;
            config = null;
            skillState = SkillState.None;
            OnDispose();
        }


        void ISkill.Cast(SkillArgs args)
        {
            if(!skillTrigger.OnCast(args))
            {
                return;
            }

            if(config.bulletId != 0)
            {
                var bullet = World.GetComponent<BulletComponent>().CreateBullet(caster, config.bulletId);
                bullet.Launch(caster.transform.position, caster.transform.forward);
            }

            if(config.buffId != null)
            {
                caster.GetComponent<BuffComponent>()?.AddBuff(config.buffId);
            }
        }

        bool ISkill.IsInCast()
        {
            return skillState == SkillState.Start || skillState == SkillState.Running;
        }
        void ISkill.Stop()
        {
            skillState = SkillState.None;
            isStartSkill = false;
            OnStop();
        }

        protected virtual void OnAwake()
        {

        }

        protected virtual void OnDispose()
        {
            
        }

        protected virtual void OnStop()
        {
            caster.GetComponent<StateMachineComponent>().ChangedState(StateType.Idle);
        }

        bool ISkill.IsCast()
        {
            return config.continueTime == 0;
        }

        void DamageByAttack()
        {
            List<Unit> colliders = new List<Unit>();
            var attackRange = caster.GetComponent<NumericComponent>().GetAsFloat(NumericType.AttackRange);
            Utils.GetUnitByRangeXZ(caster, attackRange, colliders);
            foreach (var collideUnit in colliders)
            {
                DamageCalculator.DamageBySkill(caster, collideUnit, this);
            }
        }

        int ISkill.GetConfigId()
        {
            return config.id;
        }
    }
}